package com.will.txj.wy.files.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadByteArray;
import com.github.tobato.fastdfs.domain.upload.FastFile;
import com.github.tobato.fastdfs.domain.upload.FastImageFile;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.will.txj.common.files.SysFile;
import com.will.txj.common.utils.AESCrypt;
import com.will.txj.common.utils.AesUtils;
import com.will.txj.common.utils.URLUtil;
import com.will.txj.wy.files.mapper.SysFileMapper;
import com.will.txj.wy.files.service.ISysFileService;
import com.will.txj.wy.files.utils.SftpUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author wen-yi
 * @since 2021-12-04
 */
@Slf4j
@Service
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements ISysFileService {
    @Autowired
    private FastFileStorageClient fastFileStorageClient;

    @Autowired
    private SftpUtil sftpUtil;

    @Resource
    private SysFileMapper sysFileMapper;

    private static final String SFTP = "sftp";
    private static final String key = "d084fda59e8740ce948a93d12d64e617";

    private static List<String> IMAGES = new ArrayList<>();

    static {
        // 这是缩略图所支持的格式
        IMAGES.add("JPG");
        IMAGES.add("JPEG");
        IMAGES.add("PNG");
        IMAGES.add("GIF");
    }

    @Override
    @SneakyThrows
    @Transactional
    public boolean upFile(MultipartFile file) {
        log.info("fastdfs -> 上传普通文件");
        String fileName = file.getOriginalFilename();
        long fileSize = file.getSize();
        String contentType = file.getContentType();
        String fileExtName = FilenameUtils.getExtension(fileName);
        log.info("file -> fileName: {}, fileSize: {}, contentType: {}, fileExtName: {}",
                fileName,fileSize,contentType,fileExtName);
        FastFile fastFile = new FastFile.Builder().toGroup("group1")
                .withMetaData("dateTime", LocalDateTime.now().toString())
                .withFile(file.getInputStream(), fileSize, fileExtName)
                .build();
        StorePath storePath = fastFileStorageClient.uploadFile(fastFile);
        String group = storePath.getGroup();
        String path = storePath.getPath();
        String pName = path.substring(path.lastIndexOf("/")+1);
        SysFile sysFile = SysFile.builder().fileName(fileName).contentType(contentType).fileExtName(fileExtName)
                .groupName(group).filePath(path).fileSize(Double.valueOf(fileSize)).pathName(pName)
                .metaData(JSONObject.toJSONString(fastFile.getMetaDataSet())).dis(1).cT(new Date())
                .build();
        sysFileMapper.insert(sysFile);
        log.info("file -> group: {}, pName: {}, path: {}",group,pName,path);
        return true;
    }

    @Override
    @SneakyThrows
    @Transactional
    public boolean upImageFile(MultipartFile file) {
        log.info("fastdfs -> 上传图片文件");
        String fileName = file.getOriginalFilename();
        long fileSize = file.getSize();
        String contentType = file.getContentType();
        String fileExtName = FilenameUtils.getExtension(fileName);
        log.info("file -> fileName: {}, fileSize: {}, contentType: {}, fileExtName: {}",
                fileName,fileSize,contentType,fileExtName);

        if (fileExtName!=null&&IMAGES.contains(fileExtName.toUpperCase())){
            FastImageFile fastImageFile = new FastImageFile.Builder().toGroup("group1")
                    .withFile(file.getInputStream(), fileSize, fileExtName)
                    .withMetaData("dateTime", LocalDateTime.now().toString())
                    // 默认缩略图 为原来的0.25
                    .withThumbImage().build();
            StorePath storePath = fastFileStorageClient.uploadImage(fastImageFile);
            String group = storePath.getGroup();
            String path = storePath.getPath();
            String pName = path.substring(path.lastIndexOf("/")+1);
            String thumbImagePath = fastImageFile.getThumbImagePath(storePath.getFullPath());
            SysFile sysFile = SysFile.builder().fileName(fileName).contentType(contentType).fileExtName(fileExtName)
                    .groupName(group).filePath(path).fileSize(Double.valueOf(fileSize)).pathName(pName)
                    .metaData(JSONObject.toJSONString(fastImageFile.getMetaDataSet()))
                    .thumbImagePath(thumbImagePath).dis(1).cT(new Date())
                    .build();
            sysFileMapper.insert(sysFile);
            log.info("file -> group: {}, pName: {}, path: {}, thumbImagePath: {}",group,pName,path,thumbImagePath);
            return true;
        }
        return false;
    }

    @Override
    @SneakyThrows
    @Transactional
    public boolean downloadFile(String id, HttpServletResponse response) {
        log.info("fastdfs下载文件 ->  id: {}",id);
        SysFile sysFile = sysFileMapper.selectOne(new LambdaQueryWrapper<SysFile>()
                .eq(SysFile::getId, id).eq(SysFile::getDis,1).ne(SysFile::getGroupName,SFTP));
        if (sysFile!=null){
            ServletOutputStream outputStream = null;
            try {
                String fileName = URLEncoder.encode(sysFile.getFileName(), StandardCharsets.UTF_8.toString());
                sysFile.setDownCount(sysFile.getDownCount()+1);
                sysFileMapper.updateById(sysFile);
                //返回浏览器文件信息
                response.setHeader("Content-disposition", "attachment;filename="+fileName);
                response.setCharacterEncoding("UTF-8");
                DownloadByteArray byteArray = new DownloadByteArray();
                byte[] bytes = fastFileStorageClient.downloadFile(sysFile.getGroupName(), sysFile.getFilePath(), byteArray);
                if (bytes != null){
                    outputStream = response.getOutputStream();
                    outputStream.write(bytes);
                    outputStream.flush();
                }
                return true;
            } catch (Exception e){
                log.error("下载文件异常：", e);
            } finally {
                try {
                    if (outputStream!=null)outputStream.close();
                } catch (Exception e){
                    log.error("关闭流异常", e);
                }
            }
        }
        return false;
    }

    @Override
    @Transactional
    public boolean deleteFile(String id) {
        log.info("fastdfs删除文件 ->  id: {}",id);
        if (StringUtils.isNotEmpty(id)){
            SysFile sysFile = sysFileMapper.selectOne(new LambdaQueryWrapper<SysFile>()
                    .eq(SysFile::getId, id).ne(SysFile::getGroupName,SFTP));
            if (sysFile!=null){
                fastFileStorageClient.deleteFile(sysFile.getGroupName(), sysFile.getFilePath());
                sysFileMapper.delete(new LambdaQueryWrapper<SysFile>().eq(SysFile::getId, sysFile.getId()));
                return true;
            }
        }
        return false;
    }

    @Override
    @Transactional
    public boolean deleteFileById(String id) {
        log.info("fastdfs伪删除文件 ->  id: {}",id);
        if (StringUtils.isNotEmpty(id)){
            SysFile sysFile = sysFileMapper.selectOne(new LambdaQueryWrapper<SysFile>()
                    .eq(SysFile::getId, id).eq(SysFile::getDis,1).ne(SysFile::getGroupName,SFTP));
            if (sysFile!=null){
                sysFile.setDis(0);
                sysFileMapper.updateById(sysFile);
                return true;
            }
        }
        return false;
    }

    @Override
    @SneakyThrows
    @Transactional(readOnly = true)
    public boolean showImage(String image, HttpServletResponse response) {
        String decrypt = AESCrypt.decrypt(image, key);
        Map<String, String> map = URLUtil.getUrlParamMap(decrypt);
        String id = map.get("id");
        if (id==null) return false;
        SysFile sysFile = sysFileMapper.selectOne(new LambdaQueryWrapper<SysFile>()
                .eq(SysFile::getId, id).eq(SysFile::getDis,1).ne(SysFile::getGroupName,SFTP));
        if (sysFile!=null&&IMAGES.contains(sysFile.getFileExtName().toUpperCase())){
            ServletOutputStream outputStream = null;
            try {
                //返回浏览器文件信息
                response.setContentType(sysFile.getContentType());
                DownloadByteArray byteArray = new DownloadByteArray();
                byte[] bytes = fastFileStorageClient.downloadFile(sysFile.getGroupName(), sysFile.getFilePath(), byteArray);
                if (bytes != null){
                    outputStream = response.getOutputStream();
                    outputStream.write(bytes);
                    outputStream.flush();
                }
                return true;
            } catch (Exception e){
                log.error("预览文件异常：", e);
            }finally {
                try {
                    if (outputStream!=null)outputStream.close();
                } catch (Exception e){
                    log.error("关闭流异常", e);
                }
            }
        }
        return false;
    }

    @Override
    @SneakyThrows
    @Transactional
    public boolean upFileSftp(MultipartFile file) {
        log.info("sftp -> 上传文件");
        String fileName = file.getOriginalFilename();
        long fileSize = file.getSize();
        String contentType = file.getContentType();
        String fileExtName = FilenameUtils.getExtension(fileName);
        log.info("file -> fileName: {}, fileSize: {}, contentType: {}, fileExtName: {}",
                fileName,fileSize,contentType,fileExtName);
        String pName = DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS")+"."+fileExtName;
        sftpUtil.upload(SftpUtil.DIR,pName,file.getInputStream());
        String group = "sftp";
        String path = SftpUtil.DIR+"/"+pName;
        SysFile sysFile = SysFile.builder().fileName(fileName).contentType(contentType).fileExtName(fileExtName)
                .groupName(group).filePath(path).fileSize(Double.valueOf(fileSize)).pathName(pName)
                .dis(1).cT(new Date()).build();
        sysFileMapper.insert(sysFile);
        log.info("file -> group: {}, pName: {}, path: {}",group,pName,path);
        return true;
    }

    @Override
    @SneakyThrows
    @Transactional
    public boolean downloadFileSftp(String id, HttpServletResponse response) {
        log.info("sftp下载文件 ->  id: {}",id);
        SysFile sysFile = sysFileMapper.selectOne(new LambdaQueryWrapper<SysFile>()
                .eq(SysFile::getId, id).eq(SysFile::getDis,1).eq(SysFile::getGroupName,SFTP));
        if (sysFile!=null){
            try {
                String fileName = URLEncoder.encode(sysFile.getFileName(), StandardCharsets.UTF_8.toString());
                sysFile.setDownCount(sysFile.getDownCount()+1);
                sysFileMapper.updateById(sysFile);
                //返回浏览器文件信息
                response.setHeader("Content-disposition", "attachment;filename="+fileName);
                response.setCharacterEncoding("UTF-8");
                sftpUtil.downLoad(SftpUtil.DIR, sysFile.getPathName(),response.getOutputStream());
                return true;
            } catch (Exception e){
                log.error("下载文件异常：", e);
            }
        }
        return false;
    }

    @Override
    @SneakyThrows
    @Transactional
    public boolean deleteFileSftp(String id) {
        log.info("sftp删除文件 ->  id: {}",id);
        if (StringUtils.isNotEmpty(id)){
            SysFile sysFile = sysFileMapper.selectOne(new LambdaQueryWrapper<SysFile>()
                    .eq(SysFile::getId, id).eq(SysFile::getGroupName,SFTP));
            if (sysFile!=null){
                sftpUtil.delete(SftpUtil.DIR,sysFile.getPathName());
                sysFileMapper.delete(new LambdaQueryWrapper<SysFile>().eq(SysFile::getId, sysFile.getId()));
                return true;
            }
        }
        return false;
    }

    @Override
    @Transactional
    public boolean deleteFileSftpById(String id) {
        log.info("sftp伪删除文件 ->  id: {}",id);
        if (StringUtils.isNotEmpty(id)){
            SysFile sysFile = sysFileMapper.selectOne(new LambdaQueryWrapper<SysFile>()
                    .eq(SysFile::getId, id).eq(SysFile::getDis,1).eq(SysFile::getGroupName,SFTP));
            if (sysFile!=null){
                sysFile.setDis(0);
                sysFileMapper.updateById(sysFile);
                return true;
            }
        }
        return false;
    }

    @Override
    @SneakyThrows
    @Transactional(readOnly = true)
    public boolean showImageSftp(String image, HttpServletResponse response) {
        String decrypt = AESCrypt.decrypt(image, key);
        Map<String, String> map = URLUtil.getUrlParamMap(decrypt);
        String id = map.get("id");
        if (id==null) return false;
        SysFile sysFile = sysFileMapper.selectOne(new LambdaQueryWrapper<SysFile>()
                .eq(SysFile::getId, id).eq(SysFile::getDis,1).eq(SysFile::getGroupName,SFTP));
        if (sysFile!=null&&IMAGES.contains(sysFile.getFileExtName().toUpperCase())){
            //返回浏览器文件信息
            response.setContentType(sysFile.getContentType());
            sftpUtil.downLoad(SftpUtil.DIR, sysFile.getPathName(),response.getOutputStream());
            return true;
        }
        return false;
    }
}
